package org.budo.graph.service.util;

import org.budo.graph.annotation.BudoGraph;
import org.budo.graph.annotation.SpringGraph;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.slf4j.Slf4j;
import org.budo.support.spring.context.aware.BudoApplicationContextAware;
import org.budo.support.spring.util.SpringUtil;
import org.slf4j.Logger;
import org.springframework.context.ApplicationContext;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author limin
 */
public class BudoGraphUtil {
    private static final Boolean HAS_CLASS_SPRING = ReflectUtil.hasClass("org.springframework.beans.factory.config.BeanFactoryPostProcessor");

    private static final Logger log = Slf4j.getLogger();

    private static Set<String> _BUDO_GRAPH_METHODS = null;

    private static Set<String> _SPRING_GRAPH_METHODS = null;

    public static Set<String> getBudoGraphMethods() {
        if (null == _BUDO_GRAPH_METHODS) {
            _BUDO_GRAPH_METHODS = resolveAnnotatedMethodUriSet(BudoGraph.class);
        }

        return _BUDO_GRAPH_METHODS;
    }

    public static Set<String> getSpringGraphMethods() {
        if (null == _SPRING_GRAPH_METHODS) {
            _SPRING_GRAPH_METHODS = resolveAnnotatedMethodUriSet(SpringGraph.class);
        }

        return _SPRING_GRAPH_METHODS;
    }

    /**
     * 返回所有添加了@BudoGraph注解的方法
     */
    private static Set<String> resolveAnnotatedMethodUriSet(Class<? extends Annotation> annotationType) {
        if (!HAS_CLASS_SPRING) {
            log.warn("#49 resolveAnnotatedMethodUriSet, HAS_CLASS_SPRING is false, no org.springframework.beans.factory.config.BeanFactoryPostProcessor");
            return null;
        }

        BudoApplicationContextAware applicationContextAware = BudoApplicationContextAware.getInstance();
        if (null == applicationContextAware) {
            log.warn("#36 resolveBudoGraphAnnotatedMethodUri return null, applicationContextAware is null");
            return null;
        }

        Set<String> annotatedMethodUris = new HashSet<String>();

        ApplicationContext applicationContext = applicationContextAware.getApplicationContext();

        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = getBean(applicationContext, beanDefinitionName);
            if (null == bean) {
                continue;
            }

            Class<?> beanType = bean.getClass();
            Method[] methods = beanType.getMethods();
            for (Method method : methods) {
                Set<String> annotatedMethodUrisOfMethod = annotatedMethodUris(method, annotationType);
                annotatedMethodUris.addAll(annotatedMethodUrisOfMethod);
            }
        }

        return annotatedMethodUris;
    }

    private static Set<String> annotatedMethodUris(Method method, Class<? extends Annotation> annotationType) {
        Set<String> annotatedMethodUris = new HashSet<String>();
        Method annotationPresentMethod = SpringUtil.annotationPresentMethod(method, annotationType);
        if (null != annotationPresentMethod) {
            String methodUri = ReflectUtil.methodToString(annotationPresentMethod);
            annotatedMethodUris.add(methodUri);

            log.info("#83 found @BudoGraph, method=" + method //
                    + ", annotationType=" + annotationType //
                    + ", annotationPresentMethod=" + annotationPresentMethod //
                    + ", methodUri=" + methodUri);

            // 超类的方法，也加入
            List<Method> superMethods = ReflectUtil.findSuperMethods(method);
            if (null != superMethods) {
                for (Method superMethod : superMethods) {
                    String methodUri_2 = ReflectUtil.methodToString(superMethod);
                    annotatedMethodUris.add(methodUri_2);

                    log.info("#95 found @BudoGraph, method=" + method //
                            + ", annotationType=" + annotationType //
                            + ", superMethod=" + superMethod //
                            + ", methodUri_2=" + methodUri_2);
                }
            }
        }

        // 接口申明的方法，也加入
        List<Method> interfaceMethods = ReflectUtil.findInterfaceMethods(annotationPresentMethod);
        if (null != interfaceMethods) {
            for (Method interfaceMethod : interfaceMethods) {
                String methodUri = ReflectUtil.methodToString(interfaceMethod);
                annotatedMethodUris.add(methodUri);

                log.info("#110 found @BudoGraph, method=" + method //
                        + ", annotationType=" + annotationType //
                        + ", interfaceMethod=" + interfaceMethod //
                        + ", methodUri=" + methodUri);
            }
        }

        return annotatedMethodUris;
    }

    private static Object getBean(ApplicationContext applicationContext, String beanDefinitionName) {
        try {
            return SpringUtil.getBean(applicationContext, beanDefinitionName, true);
        } catch (Throwable e) {
            log.error("#74 getBean error, beanDefinitionName=" + beanDefinitionName + ", applicationContext=" + applicationContext, e);
            return null;
        }
    }
}
